﻿using System.Collections.Generic;
using System.Xml.Linq;
using IcicleFramework.Actions;
using IcicleFramework.Components.Actions;
using IcicleFramework.Components.EntityState;
using IcicleFramework.Entities;
using IcicleFramework.GameServices;
using Microsoft.Xna.Framework;
using Newtonsoft.Json;

namespace IcicleFramework.Components.Behaviors
{
    public class BehaviorComponent : BaseComponent, IBehaviorComponent
    {
        [JsonPropertyAttribute]
        private Dictionary<string, IBehavior> behaviors;

        protected IEntityStateComponent stateComponent;

        protected IBehaviorFactory behaviorFactory;

        protected IActionComponent ActionComponent { get; set; }

        protected IActionManager actionManager;

        [JsonIgnore]
        public IEnumerator<IBehavior> Behaviors
        {
            get { return behaviors.Values.GetEnumerator(); }
        }

        public IActionFactory ActionFactory { get; protected set; }

        public BehaviorComponent()
        {
            behaviors = new Dictionary<string, IBehavior>();
        }

        public BehaviorComponent(BehaviorComponent old)
            :base(old)
        {
            behaviors = new Dictionary<string, IBehavior>();

            foreach (var behavior in old.behaviors.Values)
            {
                var newBehavior = behavior.DeepClone();
                newBehavior.Parent = this;
                this.behaviors.Add(behavior.Name, newBehavior);
            }
        }

        public override IBaseComponent DeepClone()
        {
            return new BehaviorComponent(this);
        }

        public override void Initialize()
        {
            foreach (var behavior in behaviors.Values)
                behavior.Initialize();

            actionManager = GameServiceManager.GetService<IActionManager>();
            ActionFactory = GameServiceManager.GetService<IActionFactory>();

            base.Initialize();
        }

        public override void PostInitialize()
        {
            //Track down the IEntityStateComponent and IActionComponents for this IGameObject, if they exists.
            stateComponent = this.Parent.GetComponent<IEntityStateComponent>();
            ActionComponent = Parent.GetComponent<IActionComponent>();

            foreach (var behavior in behaviors.Values)
                behavior.PostInitialize();

            base.PostInitialize();
        }

        public override void Update(GameTime gameTime)
        {
            //Update our Behavior component.
            foreach (var behavior in behaviors.Values)
                behavior.Update(gameTime);

            base.Update(gameTime);
        }

        public void FireAction(IGameAction action, ActionCompletedCallback callback = null, float delay = 0f)
        {
            if (delay > 0f)
            {
                actionManager.RegisterDelayedAction(action, delay, callback);    
            }
            else
            {
                actionManager.RegisterAction(action, callback);
            }
        }

        public void FireSingleAction(IGameObject target, string actionName, IActionData actionData = null)
        {
            if (ActionComponent != null)
            {
                ActionComponent.StartPerformingAction(target, actionName, data: actionData);
            }
        }

        public void FireContinuousActions(IGameObject target, string actionName, IActionData actionData = null)
        {
            if (ActionComponent != null)
            {
                ActionComponent.StartPerformingAction(target,actionName, data: actionData, continuous: true);
            }
        }

        public bool RequestEntityStateChanged(IEntityState newState)
        {
            bool changed = false;

            if (stateComponent != null)
            {
                changed = stateComponent.ChangeState(newState);
            }

            return changed;
        }

        public void AddBehavior(IBehavior behavior)
        {
            if (!behaviors.ContainsKey(behavior.Name))
            {
                behavior.Parent = this;
                behaviors.Add(behavior.Name, behavior);
            }
        }

        public void RemoveBehavior(IBehavior behavior)
        {
            if (behaviors.ContainsKey(behavior.Name))
            {
                behaviors.Remove(behavior.Name);
            }
        }

        public void RemoveBehavior(string name)
        {
            if (behaviors.ContainsKey(name))
            {
                behaviors.Remove(name);
            }
        }

        public override void Deserialize(XElement element)
        {
            IEnumerable<XElement> behaviorElements = element.Element("behaviors").Elements("behavior");
            behaviorFactory = GameServiceManager.GetService<IBehaviorFactory>();

            foreach (var behaviorElement in behaviorElements)
            {
                AddBehavior(behaviorFactory.GenerateBehavior(behaviorElement));
            }
        }
    }
}
